package com.alex.controller.Cn_Pubmed;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import com.alex.common.config.IndexConfig;
import com.alex.controller.Topic_Idea_Assistant.ResultVoEntity;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;

import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 作者共现网络，主体逻辑是，先对各篇论文的作者作并查集，并查集返回各作者的社团（即有连线的作者在一个类下），前发文量最大的前10个社团，再获取该社团下的各作者的相关信息，返回给前端。本方法可以轻易的修改为关键词的共现网络。
 * 2024.03.24 贾新志
 */
public class AuthNetWork {


    public static ResultVoEntity auth_net_work(List<Integer> ids) throws SQLException {
        try {
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.println(format.format(new Date()) + " 程序开始了~~~");
            long t0 = System.currentTimeMillis();

            String pmUtAidInfoTable = IndexConfig.db_article;


            System.out.println("pmOrUtIdList.size(): " + ids.size());
            /**
             * getNetWorkInfo为主方法，参数，pmOrUtIdList：表pm_ut_aid_info自增主键的集合，即ES检索的结果。pmUtAidInfoTable：表名。
             */
            if (ids.size() > 300) {
                ids = ids.subList(0, 300);
            }
            NetWorkInfo netWorkInfo = getNetWorkInfo(ids, pmUtAidInfoTable);

            List<Category> categories = netWorkInfo.getCategories();
            Set<Link> links = netWorkInfo.getLinks();
            Set<Nodes> nodes = netWorkInfo.getNodes();

            Set<AuthNetWork.Nodes> collect = new HashSet<>(nodes.stream()
                    .collect(Collectors.toMap(AuthNetWork.Nodes::getName, node -> node, (node1, node2) -> node1))
                    .values());

            for (AuthNetWork.Nodes nodesOne : collect) {
                nodesOne.setValue("" + nodesOne.getSymbolSize());
            }

            return new ResultVoEntity(categories, links, collect);
        } catch (Exception e) {
            e.printStackTrace(System.err);
            return null;
        }


// main
    }

    private static NetWorkInfo getNetWorkInfo(List<Integer> pmOrUtIdList, String pmUtAidInfoTable) throws SQLException {
        if (null == pmOrUtIdList || pmOrUtIdList.isEmpty()) {
            return new NetWorkInfo();
        }
        String spl = "ĺļ";
        List<Entity> tableData = Db.use().query("select `auth`,`id` from " + pmUtAidInfoTable + " where id in(" + CollUtil.join(new HashSet<>(pmOrUtIdList), ",") + ");");
        Set<String> authsSet = new HashSet<>();
        Map<String, Integer> authAndAuthIdMap = new HashMap<>();
        Map<Integer, String> authIdAndAuthMap = new HashMap<>();
        Map<Integer, Set<Integer>> authIdAndAidSetMap = new HashMap<>();
        List<Category> categoriesList = new ArrayList<>();
        Set<Nodes> nodesSet = new HashSet<>();
        Set<Link> linkSet = new HashSet<>();
        for (Entity entry : tableData) {
            Integer aid = entry.getInt("id");
            String auths = entry.getStr("auth");
            auths = (null == auths) ? "" : auths;
            if (null != aid && aid > 0 && auths.length() > 4) {
                try {
                    Set<String> authSet = new HashSet<>();
                    if (auths.contains(";")) {
                        String[] split = auths.split(";");
                        authSet.addAll(Arrays.asList(split));
                    } else if (auths.startsWith("[") && auths.endsWith("]")) {
                        try {
                            List<String> list = JSONObject.parseArray(auths, String.class);
                            authSet.addAll(list);

                        } catch (Exception e) {
                            System.out.println(aid + "JSON格式转换错误");
                        }

                    }
                    List<Integer> authIdList = new ArrayList<>();
                    for (String auth : authSet) {
                        if (null != auth && auth.replace(" ", "").length() > 3) {
                            auth = blankAuth(auth);
                            int authId;
                            if (authAndAuthIdMap.containsKey(auth)) {
                                authId = authAndAuthIdMap.get(auth);
                            } else {
                                authId = authAndAuthIdMap.size() + 1;
                                authIdAndAuthMap.put(authId, auth);
                                authAndAuthIdMap.put(auth, authId);
                            }
                            authIdList.add(authId);
                            putIntSetIntMap(authIdAndAidSetMap, authId, aid);
                        }
                    }
                    addLinkSet(authIdList, linkSet);
                    authsSet.add(CollUtil.join(authIdList, spl));
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
        List<Set<String>> authListSet = disjointSet(authsSet, spl);
        // 通过计算各分类下，各作者的发表论文数，以最多的作者命名。authIdAndCategorySumMap是发论文最多的作者及本类下各作者发文数的总和，用于排序获取最多的类（即社团）。authIdAndCategoryListMap是发论文最多的作者，作为本类的名称，以及本类下的各作者成员的集合。
        Map<Integer, Integer> authIdAndCategorySumMap = new HashMap<>();
        Map<Integer, List<Integer>> authIdAndCategoryListMap = new HashMap<>();
        for (Set<String> authIdStrSet : authListSet) {
            int max = 0;
            int sum = 0;
            int maxAuthId = 0;
            List<Integer> authIdList = new ArrayList<>();
            for (String authIdStr : authIdStrSet) {
                if (NumberUtil.isInteger(authIdStr)) {
                    int authId = Integer.parseInt(authIdStr);
                    if (authIdAndAidSetMap.containsKey(authId)) {
                        int size = authIdAndAidSetMap.get(authId).size();
                        sum += size;
                        if (size > max) {
                            max = size;
                            maxAuthId = authId;
                        }
                        authIdList.add(authId);
                    }
                }
            }
            authIdAndCategorySumMap.put(maxAuthId, sum);
            authIdAndCategoryListMap.put(maxAuthId, authIdList);
        }

        // 对各类下的作者以及对应的本类下各作者的发论文数量总和作排序，取前10名（即最多的前10个类（也可以叫社团）），获取各类下的作者成员的相关信息，包括作者的id号、名字、发论文的篇数、篇aid的集合等。
        Map<Integer, Integer> mapSortValueLen = mapSortValueLen(authIdAndCategorySumMap, 10);
        for (Map.Entry<Integer, Integer> entry : mapSortValueLen.entrySet()) {
            // 变量maxAuthId的意思是，本类下，发文数量最多的那个作者的id号，此id号将作为本类的名称，maxAuth是该作者的字母名称，maxAuthId是数字id号。
            Integer maxAuthId = entry.getKey();
            if (authIdAndAuthMap.containsKey(maxAuthId)) {
                Category category = new Category();
                String maxAuth = authIdAndAuthMap.get(maxAuthId);
                // 变量maxAuthId的意思是，本类下，发文数量最多的那个作者的id号，此id号对应的作者将作为本类（即社团）的名称，maxAuth是该作者的字母名称，maxAuthId是该作者的数字id号。
                category.setName(maxAuth);
                categoriesList.add(category);
                // 获取该类下，各个作者成员的相关信息。authIdAndCategoryListMap与authIdAndCategorySumMap是相同的键，即maxAuthId。
                if (authIdAndCategoryListMap.containsKey(maxAuthId)) {
                    List<Integer> authIdList = authIdAndCategoryListMap.get(maxAuthId);
                    for (Integer authId : authIdList) {
                        if (authIdAndAuthMap.containsKey(authId)) {
                            String auth = authIdAndAuthMap.get(authId);
                            Nodes nodes = new Nodes();
                            nodes.setCategory(maxAuth);
                            nodes.setId(authId);
                            if (authIdAndAidSetMap.containsKey(authId)) {
                                Set<Integer> aidSet = authIdAndAidSetMap.get(authId);
                                nodes.setSymbolSize(aidSet.size());
                                nodes.setAidList(new ArrayList<>(aidSet));
                            }
                            nodes.setName(auth);
                            nodesSet.add(nodes);
                        }
                    }
                }
            }
        }
        NetWorkInfo netWorkInfo = new NetWorkInfo();
        netWorkInfo.setCategories(categoriesList);
        netWorkInfo.setLinks(linkSet);
        netWorkInfo.setNodes(nodesSet);
        return netWorkInfo;
    }

    // 对Map的值进行排序，此排序为最大的在前面，输入一个map，再返回一个排序后的新map，Integer, Integer类型，本排序增加了过滤功能，可以设置要前多少（len）个。
    public static Map<Integer, Integer> mapSortValueLen(Map<Integer, Integer> mapName, Integer len) {
        Map<Integer, Integer> result = new LinkedHashMap<>();
        mapName.entrySet().stream().sorted(Map.Entry.<Integer, Integer>comparingByValue().reversed()).limit(len).forEachOrdered(e -> result.put(e.getKey(), e.getValue()));
        return result;
    }

    // 如果一位作者，后面两位都是单个字母，则给予合并。比如：sheftell f d->sheftell fd
    public static String blankAuth(String auth) {
        String[] split = auth.split(" ");
        if (split.length == 3) {
            if (split[1].length() == 1 && split[2].length() == 1) {
                auth = split[0] + " " + split[1] + split[2];
            }
        }
        return auth;
    }

    // 两个id之间的连线，这里采用第一个id必须大于第二个id，以避免重复连线。
    public static void addLinkSet(List<Integer> authIdList, Set<Link> linkSet) {
        if (authIdList.size() > 1) {
            for (int i = 0; i < authIdList.size(); i++) {
                int a = authIdList.get(i);
                for (int j = i + 1; j < authIdList.size(); j++) {
                    int b = authIdList.get(j);
                    if (a != b) {
                        Link link = new Link();
                        if (a > b) {
                            link.setSource(a);
                            link.setTarget(b);
                        } else {
                            link.setSource(a);
                            link.setTarget(a);
                        }
                        linkSet.add(link);
                    }
                }
            }
        }
    }

    public static void putIntSetIntMap(Map<Integer, Set<Integer>> authIdAndAidSetMap, int authId, int aid) {
        authIdAndAidSetMap.compute(authId, (k, v) -> {
            if (v == null) {
                v = new ConcurrentHashSet<>();
            }
            v.add(aid);
            return v;
        });
    }

    //并查集算法String类型_1
    public static List<Set<String>> disjointSet(Set<String> set, String spl) {
        List<Set<String>> list = new ArrayList<>();
        List<String> vs = new ArrayList<>(set);
        int size = vs.size();
        String[][] strs = new String[size][];
        for (int i = 0; i < size; i++) {
            strs[i] = vs.get(i).split(spl);
        }
        for (String[] str : strs) {
            Set<String> res = new HashSet<>(Arrays.asList(str));
            list.add(res);
        }
        int[] father = new int[size];
        for (int i = 0; i < size; i++) {
            father[i] = i;
        }
        Map<String, List<Integer>> map = new HashMap<>();
        for (int i = 0; i < size; i++) {
            for (String each : strs[i]) {
                if (!map.containsKey(each)) {
                    List<Integer> li = new ArrayList<>();
                    li.add(i);
                    map.put(each, li);
                } else {
                    map.get(each).add(i);
                }
            }
        }
        for (Map.Entry<String, List<Integer>> entry : map.entrySet()) {
            List<Integer> value = entry.getValue();
            unionHelp(value, father);
        }
        for (int i = 0; i < size; i++) {
            if (i != father[i]) {
                Set<String> dest = list.get(getFather(i, father));
                Set<String> source = list.get(i);
                dest.addAll(source);
            }
        }
        for (int i = 0; i < size; i++) {
            if (i != father[i]) {
                list.get(i).clear();
            }
        }
        list.removeIf(strings -> strings.size() == 0);
        return list;
    }

    //并查集算法String类型_2
    public static void unionHelp(List<Integer> list, int[] father) {
        int minFather = getFather(list.get(0), father);
        for (int i = 0, size = list.size(); i < size; i++) {
            father[getFather(list.get(i), father)] = minFather;
        }
    }

    //并查集算法String类型_3
    public static int getFather(int x, int[] father) {
        while (x != father[x]) {
            x = father[x];
        }
        return x;
    }

    @Data
    //最终结果的实体类
    public static class NetWorkInfo {
        private List<Category> categories = new ArrayList<>();
        private Set<Link> links = new HashSet<>();
        private Set<Nodes> nodes = new HashSet<>();
    }

    // 两个id之间的连线，为了防止重复，什么意思呢？比如，a与b，也可能会再出现一个b和a，怎么避免这种情况呢？一种是先list存放，再对list作排序。另一种是明确当存放时，要求a必须大于b，这样再用set保存，就不会出现重复了。
    @Data
    public static class Link {
        private int source = 0;
        private int target = 0;
    }

    @Data
    public static class Category {
        private String name = "";
    }

    @Data
    public static class Nodes {
        private String category = "";
        private int id = 0;
        private int symbolSize = 0;
        private String name = "";
        private String value = "";
        private List<Integer> aidList = new ArrayList<>();
    }


// end
}
